package com.yonyou.pmclouds.outter.nc;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.conf.NCInvokerConfiguration;
import com.yonyou.pmclouds.basecom.conf.NCServiceVO;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.organization.entity.OrganizationVO;
import com.yonyou.pmclouds.organization.rmiitf.OrganizationQuery;
import com.yonyou.pmclouds.plan.plandriven.entity.NcDepartmentVO;
import com.yonyou.pmclouds.plan.plandriven.entity.NcDeptQueryParamVO;
import com.yonyou.pmclouds.project.entity.ProjectVO;
import com.yonyou.pmclouds.project.rmiitf.ProjectSimpleQuery;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * nc部门查询服务，不放在server的目的是为了避免外部服务异常（服务器挂了，网络不好等）导致dubbo超时报错，影响内部功能
 *
 * @author th
 * @date 2019/12/4 15:43
 */
@Slf4j
@Component
public class NcDeptCustomerQuery {
    public static final String QUERY_DEPARTMENT_SERVICE_NAME = "DeptQueryServlet";
    /**
     * nc直连调用接口
     */
    public static final String NCC_SERVICE_SERVLET_PATH = "/service/DeptQueryServlet";
    /**
     * 友企联调用接口
     */
    public static final String NC_UBL_SERVICE = "nc.bs.pmcloud.servlet.ublimpl.DeptQueryImp4UBL";

    @Autowired
    private NCInvokerConfiguration.Invoker invoker;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private ProjectSimpleQuery projectSimpleQuery;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private OrganizationQuery organizationQuery;

    private ObjectMapper mapper = new ObjectMapper();

    public List<NcDepartmentVO> queryDepartmentFromNc(NcDeptQueryParamVO paramVO) {
        List<NcDepartmentVO> result = new ArrayList<>();
        if (paramVO == null || (StringUtils.isEmpty(paramVO.getPkOrg()) && CollectionUtils.isEmpty(paramVO.getPkDepts())
                && StringUtils.isEmpty(paramVO.getPkProject()))) {
            return result;
        }

        // 返回结果
        NCInvokerConfiguration.Result invokeResult = null;
        Map resultMap;
        try {
            // 设置nc组织
            setNcOrg(paramVO);

            Map<String, Object> requestParams = new HashMap<>();
            requestParams.put("pkDepts", paramVO.getPkDepts());
            requestParams.put("pkOrg", paramVO.getPkOrg());

            long start = System.currentTimeMillis();
            // 调用外部接口查询nc部门
            Future<NCInvokerConfiguration.Result> resultFuture = invoker.invokeAsync(
                    new NCServiceVO(QUERY_DEPARTMENT_SERVICE_NAME, NCC_SERVICE_SERVLET_PATH, NC_UBL_SERVICE),
                    requestParams, RuntimeEnvironment.getTenantId(),
                    RuntimeEnvironment.getLoginUserId(), null);

            // 阻塞，设置超时时间5秒
            invokeResult = resultFuture.get(5, TimeUnit.SECONDS);
            log.info("查询nc部门耗时：{}", System.currentTimeMillis() - start);
            if (invokeResult == null || invokeResult.getResult() == null) {
                log.error("查询部门失败, 返回结果: {}", invokeResult == null ? null : invokeResult.getResult());
                return new ArrayList<>();
            }

            if (!NCInvokerConfiguration.Result.STATUS_OK.equals(invokeResult.getStatus())) {
                log.error("查询部门失败, 返回状态: {}, 返回结果: {}", invokeResult.getStatus(), invokeResult.getResult());
                return new ArrayList<>();
            }

            resultMap = mapper.readValue(invokeResult.getResult(), Map.class);
            if (!"ok".equals(resultMap.get("resultCode"))) {
                log.error("查询部门失败，返回result：{}", invokeResult.getResult());
                return new ArrayList<>();
            }
            CollectionType listType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, NcDepartmentVO.class);
            result = mapper.readValue(JSON.toJSONString(resultMap.get("result")), listType);

            // 翻译部门的话不需要构建树
            if (CollectionUtils.isNotEmpty(paramVO.getPkDepts()))
                return result;

            return buildTree(result);
        } catch (IOException e) {
            log.error("unserialization failed, result is {}", invokeResult.getResult());
            log.error("计划待办消息发送结果反序列失败", e);
            return new ArrayList<>();
        } catch (Exception e) {
            log.error("查询部门失败, 返回结果: {}", invokeResult == null ? null : invokeResult.getResult());
            log.error("查询部门失败", e);
            return new ArrayList<>();
        }
    }

    /**
     * 根据项目或友工程组织，查询nc组织
     *
     * @param paramVO
     */
    private void setNcOrg(NcDeptQueryParamVO paramVO) throws BusinessException {
        // 计划编制，传项目主键，查询项目组织
        if (StringUtils.isNotEmpty(paramVO.getPkProject())) {
            ProjectVO projectVO = projectSimpleQuery.queryProject(paramVO.getPkProject());
            // 判断是否NC同步项目
            if (projectVO != null && StringUtils.isNotEmpty(projectVO.getPkSrc())) {
                // 设置组织
                paramVO.setPkOrg(projectVO.getPkOrg());
            }
        } else if (StringUtils.isNotEmpty(paramVO.getPkOrg())) {
            // 计划模板，传组织主键，查询来源nc组织
            OrganizationVO organizationVO = organizationQuery.queryById(paramVO.getPkOrg());
            if (organizationVO != null) {
                paramVO.setPkOrg(organizationVO.getPkSrc());
            }
        }
    }

    /**
     * 将部门构造树返回
     *
     * @param allVOs
     * @return
     */
    private List<NcDepartmentVO> buildTree(List<NcDepartmentVO> allVOs) {
        if (CollectionUtils.isEmpty(allVOs)) {
            return allVOs;
        }
        // 构造树
        Map<String, List<NcDepartmentVO>> deptChildMap = new HashMap<>();
        Map<String, String> deptCodeMap = new HashMap<>();//主键编码映射
        for (NcDepartmentVO vo : allVOs) {
            deptCodeMap.put(vo.getPkDept(), vo.getCode());
        }

        for (NcDepartmentVO vo : allVOs) {
            String parent = vo.getPkParent();
            if (StringUtils.isEmpty(parent)) {
                parent = StringUtils.EMPTY;
            } else {
                //前端需要子节点中存放父节点的编码
                vo.setParentCode(deptCodeMap.get(parent));
            }

            List<NcDepartmentVO> children = deptChildMap.get(parent);
            if (children == null) {
                children = new ArrayList<>();
                deptChildMap.put(parent, children);
            }

            children.add(vo);
        }

        for (NcDepartmentVO vo : allVOs) {
            List<NcDepartmentVO> children = deptChildMap.get(vo.getPkDept());
            vo.setChildren(children);
        }

        List<NcDepartmentVO> result = deptChildMap.get(StringUtils.EMPTY);
        if (result == null) {
            return new ArrayList<>();
        } else {
            return result;
        }
    }
}
